/** 
 *    
 *  Copyright (C) 2008 Marco Speranza <marco.speranza79@gmail.com>
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */
package org.commons.database.jconnectionpool;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.sql.ConnectionPoolDataSource;

/**
 * Classe per la creazione di uno specifico
 * <code>ConnectionPoolDataSource</code>
 * <li>DefaultConnectionPoolDataSource.ORACLE_POOL_CLASS_NAME crea e
 * inizializza la classe
 * <code> oracle.jdbc.pool.OracleConnectionPoolDataSource </code>
 * 
 * <li>DefaultConnectionPoolDataSource.MYSQL_POOL_CLASS_NAME crea e inizializza
 * la classe
 * <code> com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource </code>
 * 
 * <li>se si vuole iniziazzare una classe diversa, usare il costruttore
 * DefaultConnectionPoolDataSource(String className).
 * 
 * @author <a href="mailto:msperanza@users.sourceforge.net">Marco Speranza</a>
 * @version $Id: DataBaseManager.java,v 1.1 2006/08/10 09:48:09 msperanza Exp $
 * 
 */
public class DefaultConnectionPoolDataSource {

	static final String ORACLE_POOL_CLASS_NAME = "oracle.jdbc.pool.OracleConnectionPoolDataSource";

	static final String MYSQL_POOL_CLASS_NAME = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource";

	static final String ERROR_MSG_INIT_ERROR = "Error while initializing ConnectionPoolDataSource";

	ConnectionPoolDataSource connectionPoolDataSource = null;

	private final String className;

	/**
	 * Costruttore. instanza la classe OracleConnectionPoolDataSource
	 * 
	 */
	public DefaultConnectionPoolDataSource() {
		this(ORACLE_POOL_CLASS_NAME);
	}

	/**
	 * Costruttore.
	 * 
	 * @param className
	 *            il nome della classe da instanziare (
	 *            DefaultConnectionPoolDataSource.ORACLE_POOL_CLASS_NAME,
	 *            DefaultConnectionPoolDataSource.MYSQL_POOL_CLASS_NAME ) oppure
	 *            una stringa che rappreenta la classe da instanziare.
	 */
	public DefaultConnectionPoolDataSource(String className) {
		this.className = className;
	}

	/**
	 * Returns OracleConnectionPoolDataSource
	 * 
	 * @return ConnectionPoolDataSource
	 */

	public ConnectionPoolDataSource getConnectionPoolDataSource() {
		return connectionPoolDataSource;
	}

	/**
	 * Inizializza la classe .
	 * 
	 * @param connString
	 *            jdbc url
	 * @param username
	 *            database username
	 * @param password
	 *            user password
	 */

	public void init(String connString, String username, String password)
			throws ClassNotFoundException, Exception {
		try {
			connectionPoolDataSource = (ConnectionPoolDataSource) Class
					.forName(className).newInstance();
			if (connString != null) {
				invokeSetMethodCaseInsensitive(connectionPoolDataSource, "url",
						connString);
			}
			if (username != null) {
				invokeSetMethodCaseInsensitive(connectionPoolDataSource,
						"user", username);
			}
			if (password != null) {
				invokeSetMethodCaseInsensitive(connectionPoolDataSource,
						"password", password);
			}
		} catch (RuntimeException e) {
			throw new Exception(ERROR_MSG_INIT_ERROR, e);
		}
	}

	protected void invokeSetMethodCaseInsensitive(Object obj, String prop,
			String value) throws NoSuchMethodException,
			InvocationTargetException, IllegalAccessException {
		String alternateMethodName = null;
		Class cl = obj.getClass();
		String setMeth = "set" + prop;
		Method[] methodsList = cl.getMethods();
		boolean methodFound = false;
		int i = 0;

		for (i = 0; i < methodsList.length; ++i) {
			if (methodsList[i].getName().equalsIgnoreCase(setMeth) == true) {
				Class[] parameterTypes = methodsList[i].getParameterTypes();
				if (parameterTypes.length == 1) {
					if (parameterTypes[0].getName().equals("java.lang.String")) {
						methodFound = true;
						break;
					} else {
						alternateMethodName = methodsList[i].getName();
					}
				}
			}
		}

		if (methodFound == true) {
			Object[] params = { value };
			methodsList[i].invoke(obj, params);
			return;
		}
		if (alternateMethodName != null) {
			try {
				Class[] cldef = { Integer.TYPE };
				Method meth = cl.getMethod(alternateMethodName, cldef);
				Object[] params = { Integer.valueOf(value) };
				meth.invoke(obj, params);
				return;
			} catch (NoSuchMethodException nsmex) {
				Class[] cldef = { Boolean.TYPE };
				Method meth = cl.getMethod(alternateMethodName, cldef);
				Object[] params = { Boolean.valueOf(value) };
				meth.invoke(obj, params);
				return;
			}
		} else {
			throw new NoSuchMethodException(setMeth);
		}
	}

}